import { useEffect, useState } from 'react'

export default class StateMapper<T> {
	private getFun: () => T
	private cbs: Set<Function> = new Set([])
	constructor(getFun: () => T) {
		this.getFun = getFun
	}

	notify = () => {
		this.cbs.forEach((_) => _?.())
	}

	useMappedState = () => {
		const [_state, _setState] = useState<T>(this.getFun)
		const updateState = () => {
			_setState(this.getFun())
		}
		useEffect(() => {
			this.cbs.add(updateState)
			return () => {
				this.cbs.delete(updateState)
			}
		}, [])
		return _state
	}
}

type UpdateFunc<T> = (prev: T) => T

export class GlobalState<T> {
	private value: T
	private stateMapper: StateMapper<T>

	constructor(initValue: T) {
		this.value = initValue
		this.stateMapper = new StateMapper(this.getValue)
	}

	public getValue = () => {
		return this.value
	}

	public useValue = () => {
		return this.stateMapper.useMappedState()
	}

	public setValue = (value: T | UpdateFunc<T>) => {
		let newValue: T
		if (typeof value === 'function') {
			newValue = (value as UpdateFunc<T>)(this.value)
		} else {
			newValue = value
		}

		this.value = newValue
		this.stateMapper.notify()
	}
}
